package com.net.xpay.core.service.impl;

import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.net.xpay.common.domain.User;
import com.net.xpay.common.domain.UserProfile;
import com.net.xpay.common.domain.notify.ChargeNotifyRecord;
import com.net.xpay.common.domain.partner.PartnerCode;
import com.net.xpay.common.enums.notify.NotifyStatus;
import com.net.xpay.common.enums.user.ServicePayMode;
import com.net.xpay.common.manager.UserProfileManager;
import com.net.xpay.core.constant.PayMethod;
import com.net.xpay.core.constant.PoseidonErrorCode;
import com.net.xpay.core.model.PayTransBatchModel;
import com.net.xpay.core.model.PayTransModel;
import com.net.common.annotation.WriteTransactional;
import com.net.common.exception.BusinessException;
import com.net.common.util.PhoneUtil;
import com.net.common.util.SerializationUtil;
import com.net.common.util.StreamUtil;
import com.net.xpay.alipay.enums.IdentityType;
import com.net.xpay.common.manager.notify.ChargeNotifyRecordManager;
import com.net.xpay.common.manager.partner.PartnerCodeManager;
import com.net.xpay.core.domain.PayTrans;
import com.net.xpay.core.domain.PayTransBatch;
import com.net.xpay.core.enums.PayTransBatchStatus;
import com.net.xpay.core.enums.PayTransStatus;
import com.net.xpay.core.manager.PayTransBatchManager;
import com.net.xpay.core.manager.PayTransManager;
import com.net.xpay.core.model.*;
import com.net.xpay.core.service.OpenPayTransService;
import com.net.xpay.core.service.PayTransBatchService;
import com.net.xpay.core.utils.EmailUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.List;
import java.util.function.Function;

/**
 * 2020/6/28 10:58

 */
@Slf4j
@Service
@WriteTransactional
public class OpenPayTransServiceImpl implements OpenPayTransService {

    private static final long PAY_BATCH_MAX_COUNT = 1000L;

    @Autowired
    private PayTransBatchManager payTransBatchManager;

    @Autowired
    private PayTransManager payTransManager;

    @Autowired
    private PayTransBatchService payTransBatchService;

    @Autowired
    private ChargeNotifyRecordManager chargeNotifyRecordManager;

    @Autowired
    private UserProfileManager userProfileManager;

    @Autowired
    private PartnerCodeManager partnerCodeManager;

    @Override
    public String batchPayTrans(User user, PayTransBatchModel payModel) {
        if (Strings.isNullOrEmpty(payModel.getImportBatchNo())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "业务批次号不能为空");
        }
        if (payModel.getImportBatchNo().length() > 30) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "业务批次号不能超过30个字符");
        }
        if (!Strings.isNullOrEmpty(payModel.getTimeExpire())) {
            if (!payModel.getTimeExpire().matches("([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}):([0-9]{2})")) {
                throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "绝对超时时间格式不正确，正确格式为yyyy-MM-dd HH:mm");
            }
        }

        PayTransBatch batch = payTransBatchManager.findByImportBatchNo(user.getId(), payModel.getImportBatchNo());
        log.info("batchPayTrans batch={}", SerializationUtil.obj2String(batch));
        if (batch != null) {
            log.info("batchPayTrans exist batch={}", SerializationUtil.obj2String(batch));
            if (batch.getStatus() != PayTransBatchStatus.IMPORTED) {
                throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "批次已处理，请勿重复打款");
            }
            //还是import状态的可以再打款
            PayTransBatch payTransBatch = payTransBatchService.batchPayTrans(user, batch.getId(), payModel.getAlipayNickName(), payModel.getSignBizNo(), null, payModel.getPayMethod());
            if (payModel.getPayMethod() == null || payModel.getPayMethod().equals(PayMethod.PC)) {
                return payTransBatch.getLastPayUrl();
            }

            if (payModel.getPayMethod().equals(PayMethod.MOBILE_QR_CODE)) {
                return payTransBatch.getMobilePayQrCode();
            } else if (payModel.getPayMethod().equals(PayMethod.MOBILE_URL)) {
                return payTransBatch.getLastMobilePayUrl();
            } else {
                throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "不支持的支付方式");
            }
        }

        //检查并保存打款记录
        List<PayTrans> payTrans = checkPayTrans(payModel.getPayTrans(), payModel.getImportBatchNo(), user, payModel.getIgnoreErrorAccount());
        batch = savePayTrans(user, payModel.getImportBatchNo(), payTrans, payModel.getTimeExpire());

        //开始打款
        String payUrl;
        batch = payTransBatchService.batchPayTrans(user, batch.getId(), payModel.getAlipayNickName(), payModel.getSignBizNo(), payModel.getReturnUrl(), payModel.getPayMethod());


        if (payModel.getPayMethod() == null || payModel.getPayMethod().equals(PayMethod.PC)) {
            payUrl = batch.getLastPayUrl();
        } else if (payModel.getPayMethod().equals(PayMethod.MOBILE_QR_CODE)) {
            payUrl = batch.getMobilePayQrCode();
        } else if (payModel.getPayMethod().equals(PayMethod.MOBILE_URL)) {
            payUrl = batch.getLastMobilePayUrl();
        } else {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "不支持的支付方式");
        }

        //创建通知回调
        createNotifyRecord(payModel, user);

        return payUrl;
    }

    private void createNotifyRecord(PayTransBatchModel payModel, User user) {
        try {
            UserProfile userProfile = userProfileManager.getById(user.getId());
            if (!Boolean.TRUE.equals(userProfile.getOpenChargeNotify())) {
                return;
            }
            if (Strings.isNullOrEmpty(userProfile.getChargeNotifyUrl())) {
                return;
            }

            PayTransBatch batch = payTransBatchManager.findByImportBatchNo(user.getId(), payModel.getImportBatchNo());
            if (batch == null) {
                return;
            }

            ChargeNotifyRecord record = ChargeNotifyRecord.getInstance();
            record.setUserId(user.getId());
            record.setAppId(payModel.getAppId());
            record.setImportBatchNo(payModel.getImportBatchNo());
            record.setPayTransBatchId(batch.getId());
            record.setStatus(NotifyStatus.INIT);
            record.setNotifyCount(0);
            record.setNextNotifyTime(LocalDateTime.now());
            chargeNotifyRecordManager.save(record);
        } catch (Exception e) {
            log.error("createNotifyRecord error|payModel={}", SerializationUtil.obj2String(payModel), e);
        }
    }

    /**
     * 关闭批次
     */
    @Override
    public void closePayTransBatch(User user, String importBatchNo) {
        if (Strings.isNullOrEmpty(importBatchNo)) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "外部批次号不能为空");
        }

        PayTransBatch payTransBatch = payTransBatchManager.findByImportBatchNo(user.getId(), importBatchNo);
        if (payTransBatch == null) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "批次不存在");
        }

        payTransBatchService.closePayTransBatch(user, payTransBatch.getId());
    }

    private PayTransBatch savePayTrans(User merchant, String batchId, List<PayTrans> payTrans, String timeExpire) {
        PayTransBatch instance = PayTransBatch.getInstance();
        instance.setUserId(merchant.getId());
        instance.setPartnerId(merchant.getPartnerId());
        instance.setFatherPartnerId(merchant.getFatherPartnerId());
        instance.setFree(false);
        instance.setCommissionMode(merchant.getCommissionMode());
        instance.setMobile(merchant.getMobile());
        instance.setImportBatchNo(batchId);
        instance.setMode(merchant.getMode());
        instance.setServiceRatio(merchant.getServiceRatio());
        instance.setServiceUnitPrice(merchant.getServiceUnitPrice());
        instance.setServiceRealPrice(merchant.getServiceRealPrice());
        instance.setTimeExpire(timeExpire);
        instance.setBenefitRatio(merchant.getBenefitRatio());
        instance.setFatherBenefitRatio(merchant.getFatherBenefitRatio());
        instance.setUnitPartnerBenefit(merchant.getPartnerBenefit());
        instance.setUnitFatherPartnerBenefit(merchant.getFatherPartnerBenefit());


        if (!Strings.isNullOrEmpty(merchant.getPma())) {
            PartnerCode partnerCode = partnerCodeManager.findByPma(merchant.getPma());

            BigDecimal costServiceRatio = merchant.getCostServiceRatio();
            if (costServiceRatio == null) {
                costServiceRatio = partnerCode.getCostServiceRatio();
            }
            instance.setCostServiceRatio(costServiceRatio);
        }

        Function<PayTrans, BigDecimal> sum = ins -> new BigDecimal(ins.getTransAmount());
        instance.setTotalTransAmount(StreamUtil.sum(payTrans, sum).toString());

        if (ServicePayMode.FIXED_AMOUNT.equals(merchant.getMode())) {
            instance.setServicePrice(instance.getServiceUnitPrice() * payTrans.size());
        } else if (ServicePayMode.FIXED_RATIO.equals(merchant.getMode())) {
            instance.setServicePrice(new BigDecimal(instance.getTotalTransAmount()).multiply(instance.getServiceRatio()).multiply(new BigDecimal(100)).setScale(0, RoundingMode.UP).longValue());
        } else {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "系统错误");
        }

        instance.setTotalCount((long) payTrans.size());
        instance.setSuccessAmount("0");
        instance.setSuccessCount(0L);
        instance.setFailedAmount("0");
        instance.setFailCount(0L);
        instance.setOrderTitle("转账");
        instance.setRemark(null);
        instance.setStatus(PayTransBatchStatus.IMPORTED);
        instance.setVersion(0L);

        payTransBatchManager.save(instance);

        for (PayTrans trans : payTrans) {
            trans.setPayTransBatchId(instance.getId());
        }

        payTransManager.batchSave(payTrans);
        return instance;
    }

    private List<PayTrans> checkPayTrans(List<PayTransModel> payTransModelList, String importBatchNo, User merchant, Integer ignoreErrorAccount) {
        if (CollectionUtils.isEmpty(payTransModelList)) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "打款笔数至少一笔");
        }
        if (payTransModelList.size() > PAY_BATCH_MAX_COUNT) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "打款笔数不能超过" + PAY_BATCH_MAX_COUNT + "笔");
        }

        List<PayTrans> payTrans = Lists.newArrayList();
        List<String> importBizNos = Lists.newArrayList();
        StringBuilder errMessage = new StringBuilder();

        for (int i = 0; i < payTransModelList.size(); i++) {
            PayTransModel row = payTransModelList.get(i);
            String alipayNo = row.getIdentity();
            String name = row.getName();
            String money = row.getTransAmount();
            String importBizNo = row.getImportBizNo();
            String remark = row.getRemark();

            //一个批次订单编号不能重复
            if (!Strings.isNullOrEmpty(importBizNo)) {
                if (importBizNos.contains(importBizNo)) {
                    errMessage.append(" (第").append(i + 1).append("条:").append("订单编号重复) ");
                    continue;
                }
                importBizNos.add(importBizNo);
            }

            //买家昵称 和 支付宝账号&真实姓名
            if (row.getIdentityType() == null) {
                if ((Strings.isNullOrEmpty(alipayNo) || Strings.isNullOrEmpty(name))) {
                    errMessage.append(" (第").append(i + 1).append("条:").append("支付宝账号&真实姓名必填");
                    continue;
                }
            } else if (row.getIdentityType() == IdentityType.ALIPAY_TRADE_NO) {
                if (Strings.isNullOrEmpty(alipayNo)) {
                    errMessage.append(" (第").append(i + 1).append("条:").append("支付宝交易号必填");
                    continue;
                }
            }


            if (Strings.isNullOrEmpty(money)) {
                errMessage.append(" (第").append(i + 1).append("条:").append("打款金额不能为空) ");
                continue;
            }

            PayTrans instance = PayTrans.getInstance();
            instance.setUserId(merchant.getId());
            instance.setPartnerId(merchant.getPartnerId());
            instance.setMobile(merchant.getMobile());
            instance.setPayTransBatchId(null);
            instance.setImportBatchNo(importBatchNo);
            instance.setOutBizNo(null);
            instance.setImportBizNo(importBizNo);
            instance.setTransAmount(money);
            instance.setRemark(remark);
            instance.setName(name);
            instance.setStatus(PayTransStatus.IMPORTED);
            instance.setVersion(0L);
            instance.setIdentity(alipayNo);
            instance.setIdentityType(IdentityType.ALIPAY_LOGON_ID.getValue());

            //支付宝账号优先 可以转的金额更大
            if ((row.getIdentityType() != IdentityType.ALIPAY_TRADE_NO) && !Strings.isNullOrEmpty(alipayNo) && !Strings.isNullOrEmpty(name)) {
                if (alipayNo.contains(" ")) {
                    errMessage.append(" (第").append(i + 1).append("条:").append("支付宝账号不能有空格) ");
                    continue;
                }
                if (!alipayNo.contains("@")) {
                    if (!PhoneUtil.isEffectiveMobile(alipayNo)) {
                        errMessage.append(" (第").append(i + 1).append("条:").append("手机支付宝账号格式错误:" + alipayNo + ")");
                        continue;
                    }
                } else {
                    if (!EmailUtil.checkEmail(alipayNo)) {
                        errMessage.append(" (第").append(i + 1).append("行:").append("邮箱支付宝账号格式错误:" + alipayNo + ")");
                        continue;
                    }
                }
            }

            if (IdentityType.ALIPAY_TRADE_NO.equals(row.getIdentityType())) {
                if (alipayNo.contains(" ")) {
                    errMessage.append(" (第").append(i + 1).append("条:").append("支付宝交易号不能有空格) ");
                    continue;
                }
                if (alipayNo.length() > 32) {
                    errMessage.append(" (第").append(i + 1).append("条:").append("支付宝交易号不能超过32位) ");
                    continue;
                }
                instance.setIdentityType(IdentityType.ALIPAY_TRADE_NO.getValue());
            }

            checkIsDigit(i, money, instance, errMessage);

            payTrans.add(instance);
        }

        if (!Strings.isNullOrEmpty(errMessage.toString()) && !ignoreErrorAccount(ignoreErrorAccount)) {
            log.info("checkPayTrans error errMessage={}|importBatchNo={}|userId={}", errMessage, importBatchNo, merchant.getId());
            throw new BusinessException(PoseidonErrorCode.ALIPAY_LOGIN_ID_ERROR, errMessage.toString());
        }

        if (CollectionUtils.isEmpty(payTrans)) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "至少上传一条打款记录");
        }

        return payTrans;
    }

    private boolean ignoreErrorAccount(Integer ignoreErrorAccount) {
        return ignoreErrorAccount != null && ignoreErrorAccount == 1;
    }

    private void checkIsDigit(int i, String money, PayTrans instance, StringBuilder errMessage) {
        BigDecimal m;
        try {
            m = new BigDecimal(money);
        } catch (Exception e) {
            errMessage.append(" (第").append(i + 1).append("条:").append("打款金额格式不正确) ");
            return;
        }

        if (m.intValue() < 1) {
            errMessage.append(" (第").append(i + 1).append("条:").append("打款金额不能低于1元) ");
            return;
        }

        if (IdentityType.TAOBAO_NICKNAME.getValue().equals(instance.getIdentityType())) {
            if (m.intValue() > 1000) {
                errMessage.append(" (第").append(i + 1).append("条:").append("收款账户类型是淘宝昵称的打款金额不能大于1000元) ");
            }
        } else {
            if (m.intValue() > 50000) {
                errMessage.append(" (第").append(i + 1).append("条:").append("打款金额不能大于50000元) ");
            }
        }
    }
}
